package com.ndm.util;

import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Rect;
import android.net.http.AndroidHttpClient;
import android.util.Log;

/**
 * Various bitmap utilities
 * 
 * @author alessandro
 * 
 */
public class BitmapUtils {

	private static final Paint sPaint = new Paint();
	private static final Rect sBounds = new Rect();
	private static final Rect sOldBounds = new Rect();
	private static Canvas sCanvas = new Canvas();
	
	/**
	 * Resize a bitmap
	 * 
	 * @param input
	 * @param destWidth
	 * @param destHeight
	 * @return
	 * @throws OutOfMemoryError
	 */
	public static Bitmap resizeBitmap( final Bitmap input, int destWidth, int destHeight ) throws OutOfMemoryError {
		return resizeBitmap( input, destWidth, destHeight, 0 );
	}

	/**
	 * Resize a bitmap object to fit the passed width and height
	 * 
	 * @param input
	 *           The bitmap to be resized
	 * @param destWidth
	 *           Desired maximum width of the result bitmap
	 * @param destHeight
	 *           Desired maximum height of the result bitmap
	 * @return A new resized bitmap
	 * @throws OutOfMemoryError
	 *            if the operation exceeds the available vm memory
	 */
	public static Bitmap resizeBitmap( final Bitmap input, int destWidth, int destHeight, int rotation ) throws OutOfMemoryError {

		int dstWidth = destWidth;
		int dstHeight = destHeight;
		final int srcWidth = input.getWidth();
		final int srcHeight = input.getHeight();

		if ( rotation == 90 || rotation == 270 ) {
			dstWidth = destHeight;
			dstHeight = destWidth;
		}

		boolean needsResize = false;
		float p;
		if ( ( srcWidth > dstWidth ) || ( srcHeight > dstHeight ) ) {
			needsResize = true;
			if ( ( srcWidth > srcHeight ) && ( srcWidth > dstWidth ) ) {
				p = (float) dstWidth / (float) srcWidth;
				dstHeight = (int) ( srcHeight * p );
			} else {
				p = (float) dstHeight / (float) srcHeight;
				dstWidth = (int) ( srcWidth * p );
			}
		} else {
			dstWidth = srcWidth;
			dstHeight = srcHeight;
		}

		if ( needsResize || rotation != 0 ) {
			Bitmap output;

			if ( rotation == 0 ) {
				output = Bitmap.createScaledBitmap( input, dstWidth, dstHeight, true );
			} else {
				Matrix matrix = new Matrix();
				matrix.postScale( (float) dstWidth / srcWidth, (float) dstHeight / srcHeight );
				matrix.postRotate( rotation );
				output = Bitmap.createBitmap( input, 0, 0, srcWidth, srcHeight, matrix, true );
			}
			return output;
		} else
			return input;
	}
	
	/**
	 * 원본 이미지가 지정된 크기 안에 중앙에 배치된 새로운 이미지를 생성한다.
	 * 
	 * @param bitmap
	 *            원본 이미지
	 * @param width
	 *            너비
	 * @param height
	 *            높이
	 * @param color
	 *            배경 색상
	 * @param context
	 *            The application's context.
	 * @return 생성된 새로운 이미지
	 */
	static Bitmap centerToFit(Bitmap bitmap, int width, int height, int color,Context context) {
		final int bitmapWidth = bitmap.getWidth();
		final int bitmapHeight = bitmap.getHeight();

		if (bitmapWidth < width || bitmapHeight < height) {
			Bitmap centered = Bitmap.createBitmap(bitmapWidth < width ? width
					: bitmapWidth, bitmapHeight < height ? height
					: bitmapHeight, Bitmap.Config.RGB_565);

			Canvas canvas = new Canvas(centered);
			canvas.drawColor(color);
			canvas.drawBitmap(bitmap, (width - bitmapWidth) / 2.0f,
					(height - bitmapHeight) / 2.0f, null);

			bitmap = centered;
		}

		return bitmap;
	}

	/**
	 * thumbnail 이미지를 만들어 낸다.
	 * 
	 * @param bitmap
	 *            원본 이미지
	 * @param nIconWidth
	 *            thumbnail 너비
	 * @param nIconHeight
	 *            thumbnail 높이
	 * @param context
	 *            The application's context.
	 * 
	 * @return thumbnail 이미지
	 */
	public static Bitmap createBitmapThumbnail(Bitmap bitmap, int nIconWidth,int nIconHeight) {
		if (null == bitmap || 0 >= nIconWidth || 0 >= nIconHeight)
			return null;

		final int bitmapWidth = bitmap.getWidth();
		final int bitmapHeight = bitmap.getHeight();

		if (nIconWidth > 0 && nIconHeight > 0 && (nIconWidth < bitmapWidth || nIconHeight < bitmapHeight)) {
			final float ratio = (float) bitmapWidth / bitmapHeight;

			if (bitmapWidth > bitmapHeight) {
				nIconHeight = (int) (nIconWidth / ratio);
			} else if (bitmapHeight > bitmapWidth) {
				nIconWidth = (int) (nIconHeight * ratio);
			}

			final Bitmap.Config c = Bitmap.Config.RGB_565;
			final Bitmap bmpThumb = Bitmap.createBitmap(nIconWidth,nIconHeight, c);
			final Canvas canvas = sCanvas;
			final Paint paint = sPaint;

			canvas.setBitmap(bmpThumb);
			paint.setDither(false);
			paint.setFilterBitmap(true);
			sBounds.set((nIconWidth - nIconWidth) / 2,(nIconHeight - nIconHeight) / 2, nIconWidth, nIconHeight);
			sOldBounds.set(0, 0, bitmapWidth, bitmapHeight);
			canvas.drawBitmap(bitmap, sOldBounds, sBounds, paint);
			return bmpThumb;
		}

		return bitmap;
	}
	
	public static Bitmap fullSizeBitmap(int targetWidthHeight, String uri) {
		try {
			BitmapFactory.Options options = new BitmapFactory.Options();
			options.inJustDecodeBounds = true;
			BitmapFactory.decodeFile(uri, options);

			if (targetWidthHeight != -1)
				options.inSampleSize = computeSampleSize(options,targetWidthHeight);

			options.inJustDecodeBounds = false;
			options.inDither = false;
			options.inPreferredConfig = Bitmap.Config.ARGB_8888;

			Bitmap b = BitmapFactory.decodeFile(uri, options);

			return b;
		} catch (Exception ex) {
			Log.i("Full Size Bitmap : ", "exception occured  " + ex.toString());
			return null;
		}
	}

	public static int computeSampleSize(BitmapFactory.Options options,int target) {
		int w = options.outWidth;
		int h = options.outHeight;
		int candidateW = w / target;
		int candidateH = h / target;
		int candidate = Math.max(candidateW, candidateH);

		if (candidate == 0)
			return 1;

		if (candidate > 1) {
			if ((w > target) && (w / candidate) < target)
				candidate -= 1;
		}

		if (candidate > 1) {
			if ((h > target) && (h / candidate) < target)
				candidate -= 1;
		}
		return candidate;
	}

	public static Bitmap downloadBitmap(String strImageURL) 
    {
        final HttpClient client			= new DefaultHttpClient();
        final HttpGet getRequest 		= new HttpGet(strImageURL);
        final int IMAGE_MAX_SIZE 		= 1280;
        
        try 
        {
            HttpResponse response = client.execute(getRequest);
            final int statusCode = response.getStatusLine().getStatusCode();
            
            if (statusCode != HttpStatus.SC_OK) 
            {
                return null;
            }

            final HttpEntity entity = response.getEntity();
            
            if (entity != null) 
            {
                InputStream inputStream = null;
                
                try 
                {
                	inputStream = entity.getContent();
                	
                	BitmapFactory.Options bfo 	= new BitmapFactory.Options();
                	bfo.inJustDecodeBounds 		= true;

                    BitmapFactory.decodeStream(new FlushedInputStream(inputStream), null, bfo);
                    
                    if(bfo.outHeight * bfo.outWidth >= IMAGE_MAX_SIZE * IMAGE_MAX_SIZE)
                    {
                    	bfo.inSampleSize = (int)Math.pow(2, (int)Math.round(Math.log(IMAGE_MAX_SIZE / (double) Math.max(bfo.outHeight, bfo.outWidth)) / Math.log(0.5)));
                    }
                    bfo.inJustDecodeBounds = false;
                    
                    response = client.execute(getRequest);
                    final int nRetryStatusCode = response.getStatusLine().getStatusCode();
                    
                    if (nRetryStatusCode != HttpStatus.SC_OK) 
                    {
                        return null;
                    }
                    
                    final HttpEntity reEntity = response.getEntity();
                    
                    if (reEntity != null)
                    {
                    	InputStream reInputStream = null;
                    	
                    	try
                    	{
                    		reInputStream = reEntity.getContent();
                    		final Bitmap imgBitmap = BitmapFactory.decodeStream(new FlushedInputStream(reInputStream), null, bfo);
                            
                            return imgBitmap;
                    	}
                    	finally 
                        {
                    		 if (reInputStream != null) 
                             {
                    			 reInputStream.close();
                             }
                    		 
                    		 reEntity.consumeContent();
                        }
                    }
                } 
                finally 
                {
                    if (inputStream != null) 
                    {
                        inputStream.close();
                    }
                    
                    entity.consumeContent();
                }
            }
        } 
        catch (IOException e) 
        {
            getRequest.abort();
        } 
        catch (IllegalStateException e) 
        {
            getRequest.abort();
        } 
        catch (Exception e) 
        {
            getRequest.abort();
        } 
        finally 
        {
            if ((client instanceof AndroidHttpClient)) 
            {
                ((AndroidHttpClient)client).close();
            }
        }
        
        return null;
        
    }
	
	static class FlushedInputStream extends FilterInputStream {
		public FlushedInputStream(InputStream inputStream) {
			super(inputStream);
		}

		@Override
		public long skip(long n) throws IOException {
			long totalBytesSkipped = 0L;
			while (totalBytesSkipped < n) {
				long bytesSkipped = in.skip(n - totalBytesSkipped);
				if (bytesSkipped == 0L) {
					int b = read();
					if (b < 0) {
						break; // we reached EOF
					} else {
						bytesSkipped = 1; // we read one byte
					}
				}
				totalBytesSkipped += bytesSkipped;
			}
			return totalBytesSkipped;
		}
	}
}
